// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/common/sandbox_linux/sandbox_debug_handling_linux.h"

#include <errno.h>
#include <signal.h>
#include <stddef.h>
#include <sys/prctl.h>
#include <unistd.h>

#include "base/command_line.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/safe_sprintf.h"
#include "content/public/common/content_switches.h"

namespace content {

namespace {

    void DoChrootSignalHandler(int)
    {
        const int old_errno = errno;
        const char kFirstMessage[] = "Chroot signal handler called.\n";
        ignore_result(write(STDERR_FILENO, kFirstMessage, sizeof(kFirstMessage) - 1));

        const int chroot_ret = chroot("/");

        char kSecondMessage[100];
        const ssize_t printed = base::strings::SafeSPrintf(
            kSecondMessage, "chroot() returned %d. Errno is %d.\n", chroot_ret,
            errno);
        if (printed > 0 && printed < static_cast<ssize_t>(sizeof(kSecondMessage))) {
            ignore_result(write(STDERR_FILENO, kSecondMessage, printed));
        }
        errno = old_errno;
    }

    // This is a quick hack to allow testing sandbox crash reports in production
    // binaries.
    // This installs a signal handler for SIGUSR2 that performs a chroot().
    // In most of our BPF policies, it is a "watched" system call which will
    // trigger a SIGSYS signal whose handler will crash.
    // This has been added during the investigation of https://crbug.com/415842.
    void InstallCrashTestHandler()
    {
        struct sigaction act = {};
        act.sa_handler = DoChrootSignalHandler;
        CHECK_EQ(0, sigemptyset(&act.sa_mask));
        act.sa_flags = 0;

        PCHECK(0 == sigaction(SIGUSR2, &act, NULL));
    }

    bool IsSandboxDebuggingEnabled()
    {
        const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
        return command_line.HasSwitch(switches::kAllowSandboxDebugging);
    }

} // namespace

// static
bool SandboxDebugHandling::SetDumpableStatusAndHandlers()
{
    if (IsSandboxDebuggingEnabled()) {
        // If sandbox debugging is allowed, install a handler for sandbox-related
        // crash testing.
        InstallCrashTestHandler();
        return true;
    }

    if (prctl(PR_SET_DUMPABLE, 0) != 0) {
        PLOG(ERROR) << "Failed to set non-dumpable flag";
        return false;
    }

    return prctl(PR_GET_DUMPABLE) == 0;
}

} // namespace content
